package main

import (
	"encoding/json"
	"fmt"
	"io"
	"reflect"
	"strings"
)

// 15 | 运行时反射：字符串和结构体之间如何转换？
func main() {
	i := 3
	// 反射获取变量的值 int -> reflect.Value
	iv := reflect.ValueOf(i)
	// 反射获取变量的类型 int -> reflect.Type
	it := reflect.TypeOf(i)
	fmt.Println(iv, it)

	// reflect.Value -> int
	i1 := iv.Interface().(int)
	fmt.Println(i1)

	// 通过反射修改变量。因为 reflect.ValueOf 函数返回的是一份值的拷贝，所以我们要传入变量的指针才可以。
	ipv := reflect.ValueOf(&i)
	// 通过 Elem 方法获取指向的值
	ipv.Elem().SetInt(4)
	fmt.Println(i)

	// 修改 struct 结构体字段的值
	// 如果要修改 struct 结构体字段值的话，该字段需要是可导出的，而不是私有的，也就是该字段的首字母为大写。
	p := person{Name: "曹靖", Age: 28}
	ppv := reflect.ValueOf(&p)
	ppv.Elem().Field(0).SetString("靖哥哥")
	pv := reflect.ValueOf(p)
	fmt.Println(p)
	fmt.Println(ppv.Kind()) // ptr表示指针类型
	fmt.Println(pv.Kind())

	pt := reflect.TypeOf(p)
	// 遍历person的字段
	// 通过 FieldByName 方法获取指定的字段
	for i := 0; i < pt.NumField(); i++ {
		fmt.Println("字段:", pt.Field(i).Name)
	}
	// 遍历person的方法
	// 通过 MethodByName 方法获取指定的方法
	for i := 0; i < pt.NumMethod(); i++ {
		fmt.Println("方法:", pt.Method(i).Name)
	}

	// 尽可能通过类型断言的方式判断是否实现了某接口，而不是通过反射。下面👇只为演示反射的使用
	stringerType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
	writerType := reflect.TypeOf((*io.Writer)(nil)).Elem()
	fmt.Println("是否实现了fmt.Stringer:", pt.Implements(stringerType))
	fmt.Println("是否实现了io.Writer:", pt.Implements(writerType))

	// struct -> json
	jsonB, err := json.Marshal(p)
	if err == nil {
		fmt.Println(string(jsonB))
	}

	// json -> struct
	respJson := "{\"Name\":\"英俊潇洒风流倜傥宇宙无敌靖哥哥\",\"Age\":28}"
	json.Unmarshal([]byte(respJson), &p)
	fmt.Println(p)

	respJson = "{\"name\":\"英俊潇洒风流倜傥宇宙无敌靖哥哥666666\",\"age\":18}"
	json.Unmarshal([]byte(respJson), &p)
	fmt.Println(p)

	// 遍历person字段中key为json的tag
	for i := 0; i < pt.NumField(); i++ {
		sf := pt.Field(i)
		fmt.Printf("字段%s上,json tag为%s\n", sf.Name, sf.Tag.Get("json"))
		fmt.Printf("字段%s上,bson tag为%s\n", sf.Name, sf.Tag.Get("bson"))
	}

	// 自己实现的struct -> json
	jsonBuilder := strings.Builder{}
	jsonBuilder.WriteString("{")
	num := pt.NumField()
	for i := 0; i < num; i++ {
		jsonTag := pt.Field(i).Tag.Get("json") // 获取json tag
		jsonBuilder.WriteString("\"" + jsonTag + "\"")
		jsonBuilder.WriteString(":")
		// 获取字段的值
		jsonBuilder.WriteString(fmt.Sprintf("\"%v\"", pv.Field(i)))
		if i < num-1 {
			jsonBuilder.WriteString(",")
		}
	}
	jsonBuilder.WriteString("}")
	fmt.Println(jsonBuilder.String()) // 打印json字符串

	// 反射调用person的Print方法
	mPrint := pv.MethodByName("Print")
	agrs := []reflect.Value{reflect.ValueOf("登录")}
	mPrint.Call(agrs)
}

// 结构体
type person struct {
	Name string `json:"name" bson:"b_name"`
	Age  int    `json:"age" bson:"b_age"`
}

func (p person) String() string {
	return fmt.Sprintf("名字：%s,年龄：%d", p.Name, p.Age)
}

func (p person) Print(prefix string) {
	fmt.Printf("%s:Name is %s,Age is %d\n", prefix, p.Name, p.Age)
}
